import type { LngLatLike } from 'mapbox-gl';
import type React from 'react';
import { useEffect, useRef, useState } from 'react';
import MapboxPopup, {
  type MapboxPopupRef,
} from '@/components/MapView/components/MapboxPopup/Index';
import type { Mapless } from '@/components/MapView/Mapless';
import {
  onDefaultEvent as onMarkerDefaultEvent,
  StationsClickPanelMarker,
} from '../../components/StationsClickPanel';
import type { EmitManager, Manager } from '../../Mapless/core/manager';
import type {
  EventPayload,
  ManagerEvents,
  Station,
} from '../../Mapless/index.d';

type SupportedEvents = 'stationsSelected' | 'nodeClick' | 'movablesSelected';

type WithMapboxPopupProps<P> = P & {
  /** Mapless实例 */
  mapless: Mapless | undefined;
  /** 地图是否就绪 */
  isMapReady: boolean;
  /** mana名称 */
  manaName: string;
  /** 事件处理函数，返回弹窗内容组件所需的props和位置 */
  onEvent: (eventData: ManagerEvents[SupportedEvents][0]) => {
    props: Partial<P>;
    lngLat: LngLatLike;
  } | null;
  excludeManaNames?: string[];
  eventName?: SupportedEvents;
  destroyOnClose?: boolean;
};

/**
 * TODO 实验性组件
 * 创建一个高阶组件，用于将任意组件包装为Mapbox弹窗
 * @param WrappedComponent 要包装的组件
 * @returns 包装后的组件
 */
function withMapboxPopup<P>(WrappedComponent: React.ComponentType<P>) {
  const Component: React.FC<WithMapboxPopupProps<P>> = ({
    mapless,
    isMapReady,
    manaName,
    onEvent,
    excludeManaNames = ['SelectTool'],
    eventName = 'stationsSelected',
    destroyOnClose = false,
    ...rest
  }) => {
    const popupRef = useRef<MapboxPopupRef>(null);
    const maplessRef = useRef<Mapless>(undefined);

    const [contentProps, setContentProps] = useState<Partial<P> | null>(null);
    const [position, setPosition] = useState<LngLatLike | null>(null);

    // 处理站点选中事件
    const handleStationSelected = (...payload: unknown[]) => {
      if (!onEvent) return;
      // TODO sourceName: be careful hardcode
      const eventData = payload[0] as EventPayload;
      if (
        eventData.sourceName &&
        excludeManaNames.includes(eventData.sourceName)
      ) {
        return null;
      }
      if ((eventData.datas?.length || 0) <= 1) {
        mapless?.setOpacity(1);
        const result = onEvent(eventData);
        if (result) {
          setContentProps(result.props);
          setPosition(result.lngLat);
          popupRef.current?.show();
          setHide(false);
        } else {
          setContentProps(null);
          setPosition(null);
        }
      }
    };

    // 监听地图事件
    useEffect(() => {
      if (!isMapReady || !mapless || !manaName) return;
      maplessRef.current = mapless;

      mapless.onManaAdd((name, manager) => {
        if (name === manaName) {
          (manager as EmitManager<Record<string, unknown[]>>).on(
            eventName,
            handleStationSelected,
          );
        }
      });

      return () => {};
    }, [isMapReady, mapless, manaName, eventName, onEvent]);

    const [hide, setHide] = useState(false);

    return (
      <>
        <MapboxPopup
          ref={popupRef}
          lngLat={position as LngLatLike}
          maplessRef={maplessRef}
          onClose={() => {
            setHide(true);
          }}
        >
          {destroyOnClose && hide
            ? undefined
            : contentProps && (
                <WrappedComponent {...(rest as P)} {...contentProps} />
              )}
        </MapboxPopup>
        <StationsClickPanelMarker
          mapless={mapless}
          isMapReady={isMapReady}
          stations={[] as Station[]}
          manaName={manaName}
          destroyOnClose
          onSelected={(ss) => {
            // TODO bad stations
            handleStationSelected({
              sourceName: manaName,
              datas: ss,
              stations: ss,
            });
            mapless?.setOpacity(1);
          }}
          onEvent={(e) => {
            if ((e.datas?.length || 0) > 1) {
              const manaInstance = mapless?.getByName(manaName) as Manager;
              const datas = manaInstance?.getDatas() as Station[];
              setContentProps(null);
              setPosition(null);
              popupRef.current?.hide();
              if (datas?.length > 1) {
                mapless?.setOpacity(0.5);
              } else {
                mapless?.setOpacity(1);
              }
              return onMarkerDefaultEvent(e, datas);
            }
            return null;
          }}
        />
      </>
    );
  };

  Component.displayName = `withMapboxPopup(${WrappedComponent.displayName || WrappedComponent.name})`;
  return Component;
}

export default withMapboxPopup;
